/* Copyright (c) 2001, Swedish Institute of Computer Science.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the Institute nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * httpd.c
 *
 * Author : Adam Dunkels <adam@sics.se>
 *
 */


#include "mongoose.h"
#include "lwip/tcp.h"
#include "main.h"
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "dlmem.h"
#include <stdlib.h>
#include <stdarg.h>

#include "serial.h"

#define BUFSIZ  1024

//#define DEBUG
#if defined(DEBUG)
#define DEBUG_TRACE(x) do { \
  printf("*** %lu.%p.%s.%d: \n", \
         (unsigned long) 0, (void *) 0, \
         __func__, __LINE__); \
  printf x; \
  console_putc('\n'); \
} while (0)
#else
#define DEBUG_TRACE(x)
#endif // DEBUG


typedef int time_t;
static const char *month_names[] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

// Unified socket address. For IPv6 support, add IPv6 address structure
// in the union u.
/*
union usa {
  struct sockaddr sa;
  struct sockaddr_in sin;
};
*/

// Describes a string (chunk of memory).
struct vec {
  const char *ptr;
  size_t len;
};

// Structure used by mg_stat() function. Uses 64 bit file length.
struct mgstat {
  int is_directory;  // Directory marker
  int64_t size;      // File size
  time_t mtime;      // Modification time
};

// Describes listening socket, or socket which was accept()-ed by the master
// thread and queued for future handling by the worker thread.
struct pcb_socket {
  struct pcb_socket *next;  // Linkage
  struct tcp_pcb *sock;          // Listening socket
  //union usa lsa;        // Local socket address
  //union usa rsa;        // Remote socket address
};

enum {
  CGI_EXTENSIONS, CGI_ENVIRONMENT, PUT_DELETE_PASSWORDS_FILE, CGI_INTERPRETER,
  PROTECT_URI, AUTHENTICATION_DOMAIN, SSI_EXTENSIONS, ACCESS_LOG_FILE,
  SSL_CHAIN_FILE, ENABLE_DIRECTORY_LISTING, ERROR_LOG_FILE,
  GLOBAL_PASSWORDS_FILE, INDEX_FILES,
  ENABLE_KEEP_ALIVE, ACCESS_CONTROL_LIST, MAX_REQUEST_SIZE,
  EXTRA_MIME_TYPES, LISTENING_PORTS,
  DOCUMENT_ROOT, SSL_CERTIFICATE, NUM_THREADS, RUN_AS_USER, REWRITE,
  NUM_OPTIONS
};

static const char *config_options[] = {
  "C", "cgi_pattern", "**.cgi$|**.pl$|**.php$",
  "E", "cgi_environment", NULL,
  "G", "put_delete_passwords_file", NULL,
  "I", "cgi_interpreter", NULL,
  "P", "protect_uri", NULL,
  "R", "authentication_domain", "mydomain.com",
  "S", "ssi_pattern", "**.shtml$|**.shtm$",
  "a", "access_log_file", NULL,
  "c", "ssl_chain_file", NULL,
  "d", "enable_directory_listing", "yes",
  "e", "error_log_file", NULL,
  "g", "global_passwords_file", NULL,
  "i", "index_files", "index.html,index.htm,index.cgi",
  "k", "enable_keep_alive", "no",
  "l", "access_control_list", NULL,
  "M", "max_request_size", "2048",
  "m", "extra_mime_types", NULL,
  "p", "listening_ports", "8080",
  "r", "document_root",  ".",
  "s", "ssl_certificate", NULL,
  "t", "num_threads", "10",
  "u", "run_as_user", NULL,
  "w", "url_rewrite_patterns", NULL,
  NULL
};
#define ENTRIES_PER_CONFIG_OPTION 3


struct mg_context {
  volatile int stop_flag;       // Should we stop event loop
  char *config[NUM_OPTIONS];    // Mongoose configuration parameters
  mg_callback_t user_callback;  // User-defined callback function
  void *user_data;              // User-defined data

  struct pcb_socket *listening_sockets;
  volatile int sq_head;      // Head of the socket queue
  volatile int sq_tail;      // Tail of the socket queue

  struct pcb_socket queue[20];   // Accepted sockets
};

struct mg_connection {
  struct mg_request_info request_info;
  struct mg_context *ctx;
  struct pcb_socket client;       // Connected client
  time_t birth_time;          // Time connection was accepted
  int64_t num_bytes_sent;     // Total bytes sent to client
  int64_t content_len;        // Content-Length header value
  int64_t consumed_content;   // How many bytes of content is already read
  char *buf;                  // Buffer for received data
  char *path_info;            // PATH_INFO part of the URL
  int must_close;             // 1 if connection must be closed
  int buf_size;               // Buffer size
  int request_len;            // Size of the request + headers in a buffer
  int data_len;               // Total size of data in a buffer
  int send_len;
};

void* mg_malloc(size_t size)
{
	void* mem = NULL;
	mem  = rt_malloc(size);
	
	printf("****malloc addr[0x%x] size=%d\n",mem,size);
	memset(mem,0,size);
	assert_param(mem);
	return mem;  
}
void mg_free(void* mem_ptr)
{
	if(mem_ptr){	
		printf("****free addr[0x%x]****\n",mem_ptr);
		rt_free(mem_ptr);
	}
}
#define MAX_CONN_NUM 4
static struct mg_connection* conn_list[MAX_CONN_NUM];
static int conn_index = 0;

struct mg_connection* acquire_conn(void)
{
	struct mg_connection* conn = NULL;
	if(conn_index >= MAX_CONN_NUM) return NULL;
	conn = conn_list[conn_index++];
	printf("[acquire_conn ] index[%d] conn=0x%x\n",conn_index,conn);
	return conn;
}
void release_conn(void)
{
	if(conn_index>0)conn_index--;
	printf("[release conn] conn=%d\n",conn_index);
}
int create_conn_pool(struct mg_context* ctx)
{
	
	int buf_size = atoi(ctx->config[MAX_REQUEST_SIZE]);
	for(int i = 0; i < MAX_CONN_NUM;i++)
	{
		struct mg_connection * conn = (struct mg_connection *) mg_malloc(sizeof(*conn) + buf_size);
		if (conn == NULL) {
			return -1;
		}
		conn->buf_size = buf_size;
		conn->buf = (char *) (conn + 1);
	
		conn->birth_time = 0;
		conn->ctx = ctx;
		conn_list[i]=conn;
	}
	conn_index = 0;
	return 0;
	
}


	static void cry(struct mg_connection *conn, const char *fmt, ...) 
{

}
/**
  * @brief  callback function for handling connection errors
  * @param  arg: pointer to an argument to be passed to callback function
  * @param  err: LwIP error code   
  * @retval none
  */
static void conn_err(void *arg, err_t err)
{
  struct mg_connection *conn;
  printf("[error] connect error conn=0x%x\n",arg);
  conn = arg;
  if(conn)
  	release_conn();
}

/**
  * @brief  closes tcp connection
  * @param  pcb: pointer to a tcp_pcb struct
  * @param  hs: pointer to a http_state struct
  * @retval
  */
static void close_conn(struct tcp_pcb *pcb, struct mg_connection *conn)
{
  printf("[close] pcb=0x%x closed conn=0x%x\n",pcb,conn);
  
  tcp_arg(pcb, NULL);
  tcp_sent(pcb, NULL);
  tcp_recv(pcb, NULL);
  if(conn){
	release_conn();
  }
  tcp_close(pcb);
}

/**
  * @brief sends data found in  member "file" of a http_state struct
  * @param pcb: pointer to a tcp_pcb struct
  * @param hs: pointer to a http_state struct
  * @retval none
  */

static int send_data(struct tcp_pcb *pcb, const char* data, u32 data_len)
{
  err_t err;
  u16_t len;

  /* We cannot send more data than space available in the send
     buffer. */
  struct mg_connection* conn = (struct mg_connection*)pcb->callback_arg;
  if (tcp_sndbuf(pcb) < data_len)
  {
    len = tcp_sndbuf(pcb);
  }
  else
  {
    len = data_len;
  }
  printf("[send] pcb=0x%x len=0x%x\r\n%s \r\n",pcb,len,data);
  err = tcp_write(pcb, data, len, 0);
  if (err == ERR_OK)
  {
  	  //conn->send_len+=len;
	  //return len;
	  err = tcp_output(pcb);
	  //tcp_sent(pcb,socket_sent);
	  //if (err == ERR_OK) 
  	  	return len;
  }else if (err == ERR_MEM) {
    /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
       we do NOT return to the application thread, since ERR_MEM is
       only a temporary error! */

    /* tcp_enqueue returned ERR_MEM, try tcp_output anyway */
    //err = tcp_output(pcb);	
	//tcp_sent(pcb,socket_sent);
	
  }
  return 0;
}



/**
  * @brief tcp poll callback function
  * @param arg: pointer to an argument to be passed to callback function
  * @param pcb: pointer on tcp_pcb structure
  * @retval err_t
  */
static err_t http_poll(void *arg, struct tcp_pcb *pcb)
{
  printf("[poll] pcb=0x%x arg=0x%x\n",pcb,arg);
  if (arg == NULL)
  {
    tcp_close(pcb);
  }
  return ERR_OK;
}

/**
  * @brief callback function called after a successfull TCP data packet transmission  
  * @param arg: pointer to an argument to be passed to callback function
  * @param pcb: pointer on tcp_pcb structure
  * @param len
  * @retval err : LwIP error code
  */
static err_t http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
{
  printf("[http_sent] pcb=0x%x arg=0x%x len=0x%x\n",pcb,arg,len);

  return ERR_OK;
}
static int get_request_len(const char *buf, int buflen) {
  const char *s, *e;
  int len = 0;

  DEBUG_TRACE(("buf: %p, len: %d", buf, buflen));
  for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++)
    // Control characters are not allowed but >=128 is.
    if (!isprint(* (const unsigned char *) s) && *s != '\r' &&
        *s != '\n' && * (const unsigned char *) s < 128) {
      len = -1;
    } else if (s[0] == '\n' && s[1] == '\n') {
      len = (int) (s - buf) + 2;
    } else if (s[0] == '\n' && &s[1] < e &&
        s[1] == '\r' && s[2] == '\n') {
      len = (int) (s - buf) + 3;
    }

  return len;
}

static int read_request(struct pbuf *p, char *buf, int bufsiz,
                        int *nread) {
  int request_len = 0;

	struct pbuf *q;
	u32 len = 0;
	u32 copy_len = 0;
  
  //fix me 
	for(q=p; q != NULL; q = q->next) 
	{
	  copy_len = (q->len > (bufsiz-len) )? bufsiz-len:q->len;
	  memcpy(buf + len, q->payload, copy_len);
	  len += copy_len;	  
	}

	*nread += len;
	request_len = get_request_len(buf, *nread);
/*
  while (*nread < bufsiz && request_len == 0) {
  	
    n = pull(fp, sock, ssl, buf + *nread, bufsiz - *nread);
    if (n <= 0) {
      break;
    } else {
      *nread += n;
      request_len = get_request_len(buf, *nread);
    }
  }
*/
  return request_len;
}
#define INT_MAX 0x100000
// Write data to the IO channel - opened file descriptor, socket or SSL
// descriptor. Return number of bytes written.
static int64_t push(struct tcp_pcb *sock,  const char *buf,
                    int64_t len) {
  int64_t sent;
  int n, k;

  sent = 0;

  #if 0
  while (sent < len) {

    // How many bytes we send in this iteration
    k = len - sent > INT_MAX ? INT_MAX : (int) (len - sent);

 
    //n = send(sock, buf + sent, (size_t) k, MSG_NOSIGNAL);
	n  = send_data(sock,buf + sent, (size_t) k);
	
    if (n < 0)
      break;

    sent += n;
  }
#endif
  n  = send_data(sock,buf , (size_t) len);

  return n;
}

int mg_write(struct mg_connection *conn, const void *buf, size_t len) {
  return (int) push(conn->client.sock, (const char *) buf,
                    (int64_t) len);
}
static int mg_vsnprintf(struct mg_connection *conn, char *buf, size_t buflen,
                        const char *fmt, va_list ap) {
  int n;

  if (buflen == 0)
    return 0;

  n = vsnprintf(buf, buflen, fmt, ap);

  if (n < 0) {
    cry(conn, "vsnprintf error");
    n = 0;
  } else if (n >= (int) buflen) {
    cry(conn, "truncating vsnprintf buffer: [%.*s]",
        n > 200 ? 200 : n, buf);
    n = (int) buflen - 1;
  }
  buf[n] = '\0';

  return n;
}

static int mg_snprintf(struct mg_connection *conn, char *buf, size_t buflen,
                       const char *fmt, ...) {
  va_list ap;
  int n;

  va_start(ap, fmt);
  n = mg_vsnprintf(conn, buf, buflen, fmt, ap);
  va_end(ap);

  return n;
}
char buf[BUFSIZ];

int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
  int len;
  va_list ap;
  memset(buf,0,BUFSIZ);
  va_start(ap, fmt);
  len = mg_vsnprintf(conn, buf, sizeof(buf), fmt, ap);
  va_end(ap);

  return mg_write(conn, buf, (size_t)len);
}

static void *call_user(struct mg_connection *conn, enum mg_event event) {
  conn->request_info.user_data = conn->ctx->user_data;
  return conn->ctx->user_callback == NULL ? NULL :
    conn->ctx->user_callback(event, conn, &conn->request_info);
}
// Return HTTP header value, or NULL if not found.
static const char *get_header(const struct mg_request_info *ri,
                              const char *name) {
  int i;

  for (i = 0; i < ri->num_headers; i++)
    if (!mg_strcasecmp(name, ri->http_headers[i].name))
      return ri->http_headers[i].value;

  return NULL;
}

const char *mg_get_header(const struct mg_connection *conn, const char *name) {
  return get_header(&conn->request_info, name);
}

static int should_keep_alive(const struct mg_connection *conn) {
  const char *http_version = conn->request_info.http_version;
  const char *header = mg_get_header(conn, "Connection");
  return (!conn->must_close &&
          !conn->request_info.status_code != 401 &&
          !mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") &&
          (header == NULL && http_version && !strcmp(http_version, "1.1"))) ||
          (header != NULL && !mg_strcasecmp(header, "keep-alive"));
}

static const char *suggest_connection_header(const struct mg_connection *conn) {
  return should_keep_alive(conn) ? "keep-alive" : "close";
}

static void send_http_error(struct mg_connection *conn, int status,
                            const char *reason, const char *fmt, ...) {
  char buf[BUFSIZ];
  va_list ap;
  int len;

  conn->request_info.status_code = status;

  if (call_user(conn, MG_HTTP_ERROR) == NULL) {
    buf[0] = '\0';
    len = 0;

    // Errors 1xx, 204 and 304 MUST NOT send a body
    if (status > 199 && status != 204 && status != 304) {
      len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
      cry(conn, "%s", buf);
      buf[len++] = '\n';

      va_start(ap, fmt);
      len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap);
      va_end(ap);
    }
    DEBUG_TRACE(("[%s]", buf));

    mg_printf(conn, "HTTP/1.1 %d %s\r\n"
              "Content-Type: text/plain\r\n"
              "Content-Length: %d\r\n"
              "Connection: %s\r\n\r\n", status, reason, len,
              suggest_connection_header(conn));
    conn->num_bytes_sent += mg_printf(conn, "%s", buf);
  }
}
// Skip the characters until one of the delimiters characters found.
// 0-terminate resulting word. Skip the delimiter and following whitespaces if any.
// Advance pointer to buffer to the next word. Return found 0-terminated word.
// Delimiters can be quoted with quotechar.
static char *skip_quoted(char **buf, const char *delimiters, const char *whitespace, char quotechar) {
  char *p, *begin_word, *end_word, *end_whitespace;

  begin_word = *buf;
  end_word = begin_word + strcspn(begin_word, delimiters);

  // Check for quotechar
  if (end_word > begin_word) {
    p = end_word - 1;
    while (*p == quotechar) {
      // If there is anything beyond end_word, copy it
      if (*end_word == '\0') {
        *p = '\0';
        break;
      } else {
        size_t end_off = strcspn(end_word + 1, delimiters);
        memmove (p, end_word, end_off + 1);
        p += end_off; // p must correspond to end_word - 1
        end_word += end_off + 1;
      }
    }
    for (p++; p < end_word; p++) {
      *p = '\0';
    }
  }

  if (*end_word == '\0') {
    *buf = end_word;
  } else {
    end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace);

    for (p = end_word; p < end_whitespace; p++) {
      *p = '\0';
    }

    *buf = end_whitespace;
  }

  return begin_word;
}

// Simplified version of skip_quoted without quote char
// and whitespace == delimiters
static char *skip(char **buf, const char *delimiters) {
  return skip_quoted(buf, delimiters, delimiters, 0);
}
static int is_valid_http_method(const char *method) {
  return !strcmp(method, "GET") || !strcmp(method, "POST") ||
    !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
    !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
    !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND");
}
// Parse HTTP headers from the given buffer, advance buffer to the point
// where parsing stopped.
static void parse_http_headers(char **buf, struct mg_request_info *ri) {
  int i;

  for (i = 0; i < (int) ARRAY_SIZE(ri->http_headers); i++) {
    ri->http_headers[i].name = skip_quoted(buf, ":", " ", 0);
    ri->http_headers[i].value = skip(buf, "\r\n");
    if (ri->http_headers[i].name[0] == '\0')
      break;
    ri->num_headers = i + 1;
  }
}

// Parse HTTP request, fill in mg_request_info structure.
static int parse_http_request(char *buf, struct mg_request_info *ri) {
  int status = 0;

  // RFC says that all initial whitespaces should be ingored
  while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
    buf++;
  }

  ri->request_method = skip(&buf, " ");
  ri->uri = skip(&buf, " ");
  ri->http_version = skip(&buf, "\r\n");

  if (is_valid_http_method(ri->request_method) &&
      strncmp(ri->http_version, "HTTP/", 5) == 0) {
    ri->http_version += 5;   // Skip "HTTP/"
    parse_http_headers(&buf, ri);
    status = 1;
  }

  return status;
}
static int is_valid_uri(const char *uri) {
  // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
  // URI can be an asterisk (*) or should start with slash.
  return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
}
static size_t url_decode(const char *src, size_t src_len, char *dst,
                         size_t dst_len, int is_form_url_encoded) {
  size_t i, j;
  int a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')

  for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
    if (src[i] == '%' &&
        isxdigit(* (const unsigned char *) (src + i + 1)) &&
        isxdigit(* (const unsigned char *) (src + i + 2))) {
      a = tolower(* (const unsigned char *) (src + i + 1));
      b = tolower(* (const unsigned char *) (src + i + 2));
      dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
      i += 2;
    } else if (is_form_url_encoded && src[i] == '+') {
      dst[j] = ' ';
    } else {
      dst[j] = src[i];
    }
  }

  dst[j] = '\0'; // Null-terminate the destination

  return j;
}
#define IS_DIRSEP_CHAR(c) ((c) == '/')

static void remove_double_dots_and_double_slashes(char *s) {
  char *p = s;

  while (*s != '\0') {
    *p++ = *s++;
    if (IS_DIRSEP_CHAR(s[-1])) {
      // Skip all following slashes and backslashes
      while (IS_DIRSEP_CHAR(s[0])) {
        s++;
      }

      // Skip all double-dots
      while (*s == '.' && s[1] == '.') {
        s += 2;
      }
    }
  }
  *p = '\0';
}
#if 0
static int convert_uri_to_file_name(struct mg_connection *conn, char *buf,
                                    size_t buf_len, struct mgstat *st) {
  struct vec a, b;
  const char *rewrite, *uri = conn->request_info.uri;
  char *p;
  int match_len, stat_result;

  buf_len--;  // This is because memmove() for PATH_INFO may shift part
              // of the path one byte on the right.
  mg_snprintf(conn, buf, buf_len, "%s%s", conn->ctx->config[DOCUMENT_ROOT],
              uri);

  rewrite = conn->ctx->config[REWRITE];
  while ((rewrite = next_option(rewrite, &a, &b)) != NULL) {
    if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) {
      mg_snprintf(conn, buf, buf_len, "%.*s%s", b.len, b.ptr, uri + match_len);
      break;
    }
  }

#if defined(_WIN32) && !defined(__SYMBIAN32__)
  //change_slashes_to_backslashes(buf);
#endif // _WIN32

  if ((stat_result = mg_stat(buf, st)) != 0) {
    // Support PATH_INFO for CGI scripts.
    for (p = buf + strlen(buf); p > buf + 1; p--) {
      if (*p == '/') {
        *p = '\0';
        if (match_prefix(conn->ctx->config[CGI_EXTENSIONS],
                         strlen(conn->ctx->config[CGI_EXTENSIONS]), buf) > 0 &&
            (stat_result = mg_stat(buf, st)) == 0) {
          conn->path_info = p + 1;
          memmove(p + 2, p + 1, strlen(p + 1));
          p[1] = '/';
          break;
        } else {
          *p = '/';
          stat_result = -1;
        }
      }
    }
  }

  return stat_result;
}
#endif
#define PATH_MAX 64
static void handle_request(struct mg_connection *conn) {
  struct mg_request_info *ri = &conn->request_info;
  char path[PATH_MAX];
  int stat_result, uri_len;
  struct mgstat st;
#if 0
  if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
    * conn->request_info.query_string++ = '\0';
  }
  uri_len = strlen(ri->uri);
  url_decode(ri->uri, (size_t)uri_len, ri->uri, (size_t)(uri_len + 1), 0);
  remove_double_dots_and_double_slashes(ri->uri);
  stat_result = convert_uri_to_file_name(conn, path, sizeof(path), &st);

  DEBUG_TRACE(("uri === %s", ri->uri));
  #endif
  //send_http_error(conn, 413, "Request Too Large123", "");
  if (call_user(conn, MG_NEW_REQUEST) != NULL) {
    // Do nothing, callback has served the request
  } 
}


/**
  * @brief callback function for handling TCP HTTP traffic
  * @param arg: pointer to an argument structure to be passed to callback function
  * @param pcb: pointer to a tcp_pcb structure
  * @param p: pointer to a packet buffer
  * @param err: LwIP error code
  * @retval err
  */
static err_t http_recv(void *arg, struct tcp_pcb *pcb,  struct pbuf *p, err_t err)
{
    struct mg_connection *conn = (struct mg_connection *)arg;
	 const char *cl;
	struct mg_request_info *ri = &conn->request_info;
	if (err == ERR_OK && p != NULL && conn!=NULL)
	{
	  /* Inform TCP that we have taken the data */
	    printf("[recv] pcb=0x%x conn=0x%x len=%d\n",pcb,conn,p->tot_len);
	  	tcp_recved(pcb, p->tot_len);		
		//send_http_error(conn, 413, "Request Too Large", "");

	  #if 1
		if((conn->request_len = get_request_len(conn->buf, conn->data_len)) == 0) {
			conn->request_len = read_request(p, 
			conn->buf, conn->buf_size, &conn->data_len);
		}
		assert_param(conn->data_len >= conn->request_len);
		if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
			send_http_error(conn, 413, "Request Too Large", "");
			goto exit;
		} if (conn->request_len <= 0) {
			goto exit;
		}
		conn->buf[conn->request_len - 1] = '\0';
	    if (!parse_http_request(conn->buf, ri) || !is_valid_uri(ri->uri)) {
	      // Do not put garbage in the access log, just send it back to the client
	      send_http_error(conn, 400, "Bad Request",
	          "Cannot parse HTTP request: [%.*s]", conn->data_len, conn->buf);
	    } else if (strcmp(ri->http_version, "1.0") &&
	               strcmp(ri->http_version, "1.1")) {
			      // Request seems valid, but HTTP version is strange
			      send_http_error(conn, 505, "HTTP version not supported", "");
	    } else {
				cl = get_header(ri, "Content-Length");
				conn->content_len = cl == NULL ? -1 : strtoll(cl, NULL, 10);
				conn->birth_time = 0;
				
				//send_http_error(conn, 413, "Request Too Large123", "");
				handle_request(conn);
				call_user(conn, MG_REQUEST_COMPLETE);
	    }
	
	#endif
	}

exit:
	if(err == ERR_OK){
		if(p) pbuf_free(p);
	}else{
		printf("[recv] err=%d\n",err);
	}
	
	close_conn(pcb,conn);  
    return ERR_OK;
}
static void produce_socket(struct mg_context *ctx, const struct pcb_socket *sp) {

  // If the queue is full, wait
  while (ctx->stop_flag == 0 &&
         ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
  }

  if (ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue)) {
    // Copy socket to the queue and increment head
    ctx->queue[ctx->sq_head % ARRAY_SIZE(ctx->queue)] = *sp;
    ctx->sq_head++;
    DEBUG_TRACE(("queued socket %d", sp->sock));
  }
}
static void reset_per_request_attributes(struct mg_connection *conn) {
  struct mg_request_info *ri = &conn->request_info;

  // Reset request info attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
  ri->remote_user = ri->request_method = ri->uri = ri->http_version =
    conn->path_info = NULL;
  ri->num_headers = 0;
  ri->status_code = -1;

  conn->num_bytes_sent = conn->consumed_content = 0;
  conn->content_len = -1;
  conn->request_len = conn->data_len = 0;
  conn->send_len = 0;
  conn->must_close = 0;
}

/**
  * @brief  callback function on TCP connection setup ( on port 80)
  * @param  arg: pointer to an argument structure to be passed to callback function
  * @param  pcb: pointer to a tcp_pcb structure
  * &param  err: Lwip stack error code
  * @retval err
  */
static err_t mongoo_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{

	struct mg_context* ctx = (struct mg_context*)arg;
	struct mg_connection *conn;

	conn = acquire_conn();
	if(conn == NULL){
		printf("[accept] reject no conn!!");
		tcp_close(pcb);
		return ERR_CLSD;
	}


  /* Tell TCP that we wish to be informed of incoming data by a call
     to the http_recv() function. */
  tcp_recv(pcb, http_recv);

  tcp_err(pcb, conn_err);

  tcp_poll(pcb, http_poll, 10);

  
  conn->request_info.remote_port = pcb->remote_port;
  conn->request_info.remote_ip   = pcb->remote_ip.addr;
  conn->client.sock = pcb;
  
  reset_per_request_attributes(conn);
  //produce_socket((struct mg_context*)arg,&sp);
  tcp_arg(pcb,conn);
  printf("[accept] pcb=0x%x conn=0x%x ip[%d.%d.%d.%d] port:%d\n",pcb,conn,ip4_addr1(&(pcb->remote_ip)),ip4_addr2(&(pcb->remote_ip)),\
  							ip4_addr3(&(pcb->remote_ip)),ip4_addr4(&(pcb->remote_ip)),\
  							pcb->remote_port);
  return ERR_OK;
}

const char *mg_version(void) {
  return "1.0";
}

static void mg_strlcpy(register char *dst, register const char *src, size_t n) {
  for (; *src != '\0' && n > 1; n--) {
    *dst++ = *src++;
  }
  *dst = '\0';
}

static int lowercase(const char *s) {
  return tolower(* (const unsigned char *) s);
}

static int mg_strcasecmp(const char *s1, const char *s2) {
  int diff;

  do {
    diff = lowercase(s1++) - lowercase(s2++);
  } while (diff == 0 && s1[-1] != '\0');

  return diff;
}

static char * mg_strndup(const char *ptr, size_t len) {
  char *p;

  if ((p = (char *) mg_malloc(len + 1)) != NULL) {
    mg_strlcpy(p, ptr, len + 1);
  }

  return p;
}

static char * mg_strdup(const char *str) {
  return mg_strndup(str, strlen(str));
}

static int get_option_index(const char *name) {
  int i;

  for (i = 0; config_options[i] != NULL; i += ENTRIES_PER_CONFIG_OPTION) {
    if (strcmp(config_options[i], name) == 0 ||
        strcmp(config_options[i + 1], name) == 0) {
      return i / ENTRIES_PER_CONFIG_OPTION;
    }
  }
  return -1;
}
static void free_context(struct mg_context *ctx) {
  int i;

  // Deallocate config parameters
  for (i = 0; i < NUM_OPTIONS; i++) {
    if (ctx->config[i] != NULL)
      mg_free(ctx->config[i]);
  }

  // Deallocate context itself
  mg_free(ctx);
}
static struct mg_connection *fc(struct mg_context *ctx) {
  static struct mg_connection fake_connection;
  fake_connection.ctx = ctx;
  return &fake_connection;
}

struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
                            const char **options) {
                            
	struct mg_context *ctx;
	const char *name, *value, *default_value;
	int i;


	ctx = (struct mg_context *) mg_malloc(sizeof(*ctx));

	ctx->user_callback = user_callback;
	ctx->user_data = user_data;

	
	while (options && (name = *options++) != NULL) {
	  if ((i = get_option_index(name)) == -1) {
		cry(fc(ctx), "Invalid option: %s", name);
		free_context(ctx);
		return NULL;
	  } else if ((value = *options++) == NULL) {
		cry(fc(ctx), "%s: option value cannot be NULL", name);
		free_context(ctx);
		return NULL;
	  }
	  if (ctx->config[i] != NULL) {
		cry(fc(ctx), "%s: duplicate option", name);
	  }
	  ctx->config[i] = mg_strdup(value);
	  DEBUG_TRACE(("[%s] -> [%s]", name, value));
	}
	
	// Set default value if needed
	for (i = 0; config_options[i * ENTRIES_PER_CONFIG_OPTION] != NULL; i++) {
	  default_value = config_options[i * ENTRIES_PER_CONFIG_OPTION + 2];
	  if (ctx->config[i] == NULL && default_value != NULL) {
		ctx->config[i] = mg_strdup(default_value);
		DEBUG_TRACE(("Setting default: [%s] -> [%s]",
					 config_options[i * ENTRIES_PER_CONFIG_OPTION + 1],
					 default_value));
	  }
	}

	return ctx;
	
}
static int i=0;
char mg_buf[256]={0,};
#include "datetime.h"
const char* html[]={"Content-Type:text/html\n\n", 
				  "<HTML>\n", 
				  "<HEAD>\n<TITLE>Hellotitle</TITLE>\n</HEAD>\n",
				  "<BODY>\nhello</BODY>\n</HTML>"};
static void *event_handler(enum mg_event event,
                           struct mg_connection *conn,
                           const struct mg_request_info *request_info) {
	if (event == MG_NEW_REQUEST) {
	// Echo requested URI back to the client
    TDateTime dt;
	int n = 0;
	get_datetime(&dt);
	
	memset(html,0,256);
	
//              "Content-Length: %d\r\n"
	//mg_snprintf(conn,textmsg,128,"%d-%d-%d %d:%d:%d",dt.year,dt.mon,dt.day,dt.hour,dt.min,dt.sec);
    mg_printf(conn, "HTTP/1.1 %d %s\r\n"
              "Content-Type: text/plain\r\n"
              "Connection: %s\r\n\r\n", 200, "OK",
              suggest_connection_header(conn));
	n=mg_snprintf(conn,mg_buf,"%s",html);
    mg_printf(conn,"%s",mg_buf);
	//conn->num_bytes_sent += mg_printf(conn, "%s", textmsg);
	
	//mg_printf(conn, "HTTP/1.1 200 OK\r\n"
	//		 "Content-Type: text/plain\r\n\r\n"
	//		 "%d", i++);
	return "";  // Mark as processed
	} else {
	return NULL;
	}

}
static const char *options[] = {
  "document_root", "html",
  "listening_ports", "8081,8082s",
  "ssl_certificate", "ssl_cert.pem",
  "num_threads", "5",
  NULL
};

/**
  * @brief  intialize HTTP webserver  
  * @param  none
  * @retval none
  */
int mongoo_start(void)
{
  struct tcp_pcb *pcb;
  
  struct mg_context *ctx;

  /*create new pcb*/
  pcb = tcp_new();
  
  if (!pcb)
  {
    return 0;
  }
  /* bind HTTP traffic to pcb */
  tcp_bind(pcb, IP_ADDR_ANY, 80);
  /* start listening on port 80 */
  pcb = tcp_listen(pcb);
  /* define callback function for TCP connection setup */
  tcp_accept(pcb, mongoo_accept);

  	
  // Setup and start Mongoose
  ctx = mg_start(event_handler, NULL, options);
  if(-1==create_conn_pool(ctx)){
  	
  }
  tcp_arg(pcb,ctx);
  network_set_ok(1);
  
  return 1;	
  
}



/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/
